﻿using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;

namespace ThreeDWorld
{
    public class ContentReader : BinaryReader
    {
        public ContentReader(ContentManager content, Stream stream)
            : base(stream)
        {
            Content = content;
        }

        public ContentManager Content
        {
            get;
            private set;
        }

        public Vector2 ReadVector2()
        {
            return new Vector2(ReadSingle(), ReadSingle());
        }

        public Vector3 ReadVector3()
        {
            return new Vector3(ReadSingle(), ReadSingle(), ReadSingle());
        }

        public Vector4 ReadVector4()
        {
            return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
        }

        public Matrix ReadMatrix()
        {
            return new Matrix(
                ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle(),
                ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle(),
                ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle(),
                ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()
                );
        }

        public VertexElement ReadVertexElement()
        {
            return new VertexElement(ReadInt32(), (VertexElementFormat)ReadInt32(), (VertexElementUsage)ReadInt32(), ReadInt32());
        }

        public BoundingBox ReadBoundingBox()
        {
            return new BoundingBox(ReadVector3(), ReadVector3());
        }

        public Rectangle ReadRectangle()
        {
            return new Rectangle(ReadInt32(), ReadInt32(), ReadInt32(), ReadInt32());
        }

        public byte[] ReadByteArray(int length)
        {
            byte[] array = new byte[length];
            int i = 0;

            while (true)
            {
                int n = BaseStream.Read(array, i, length);

                if (n == 0)
                    throw new Exception("End of stream");
                length -= n;
                i += n;
                if (length == 0)
                    break;
            }
            return array;
        }

        public byte[] ReadByteArray()
        {
            return ReadByteArray((int)(BaseStream.Length - BaseStream.Position));
        }

        public short[] ReadInt16Array()
        {
            short[] array = new short[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadInt16();
            }
            return array;
        }

        public int[] ReadInt32Array()
        {
            int[] array = new int[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadInt32();
            }
            return array;
        }

        public float[] ReadSingleArray()
        {
            float[] array = new float[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadSingle();
            }
            return array;
        }

        public VertexElement[] ReadVertexElementArray()
        {
            VertexElement[] array = new VertexElement[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadVertexElement();
            }
            return array;
        }

        public Rectangle[] ReadRectangleArray()
        {
            Rectangle[] array = new Rectangle[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadRectangle();
            }
            return array;
        }

        public T[] ReadArray<T>()
        {
            T[] array = new T[ReadInt32()];

            for (int i = 0; i != array.Length; i++)
            {
                array[i] = ReadObject<T>();
            }
            return array;
        }

        public VertexBuffer ReadVertexBuffer()
        {
            VertexBuffer vertexBuffer = new VertexBuffer(Content.Game.GraphicsDevice, new VertexDeclaration(ReadVertexElementArray()), ReadInt32(), BufferUsage.WriteOnly);

            vertexBuffer.SetData<float>(ReadSingleArray());

            return vertexBuffer;
        }

        public IndexBuffer ReadIndexBuffer()
        {
            IndexBuffer indexBuffer = new IndexBuffer(Content.Game.GraphicsDevice, IndexElementSize.SixteenBits, ReadInt32(), BufferUsage.WriteOnly);

            indexBuffer.SetData<short>(ReadInt16Array());

            return indexBuffer;
        }

        public T ReadExternalReference<T>()
        {
            return Content.Load<T>(ReadString());
        }

        public T ReadObject<T>()
        {
            if (typeof(T) == typeof(PixelShader))
            {
                return (T)(object)PixelShader.FromStream(Content.Game.GraphicsDevice, BaseStream);
            }
            else if (typeof(T) == typeof(VertexShader))
            {
                return (T)(object)VertexShader.FromStream(Content.Game.GraphicsDevice, BaseStream);
            }
            else
            {
                Type type = Type.GetType(ReadString() + ", Version=1.0.0.0");
                IContentTypeReader typeReader = Activator.CreateInstance(type) as IContentTypeReader;

                return (T)typeReader.ReadObject(this);
            }
        }
    }
}
